#############################################################################
#Copyright (c) 2024,
#
#Distributed under the terms of the BSD 3-Clause License.
#
#The full license is in the file LICENSE, distributed with this software.
#############################################################################

cmake_minimum_required(VERSION 3.4.3)
project(xeus-xlang)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/install")
set(XEUS_XLANG_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Versionning
# ===========

file(STRINGS "${XEUS_XLANG_INCLUDE_DIR}/xeus-xlang/xeus_xlang_config.hpp" version_defines
     REGEX "#define XEUS_XLANG_VERSION_(MAJOR|MINOR|PATCH)")
foreach (ver ${version_defines})
    if (ver MATCHES "#define XEUS_XLANG_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$")
        set(XEUS_XLANG_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "")
    endif ()
endforeach ()
set(${PROJECT_NAME}_VERSION
    ${XEUS_XLANG_VERSION_MAJOR}.${XEUS_XLANG_VERSION_MINOR}.${XEUS_XLANG_VERSION_PATCH})
message(STATUS "Building xeus-xlang v${${PROJECT_NAME}_VERSION}")

# Configuration
# =============

include(GNUInstallDirs)

if (NOT DEFINED XEUS_XLANG_KERNELSPEC_PATH)
    set(XEUS_XLANG_KERNELSPEC_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}/")
endif ()

configure_file (
    "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xlang_kernel/kernel.json.in"
    "${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels/xlang_kernel/kernel.json"
)

# Build options
# =============

option(XEUS_XLANG_BUILD_STATIC "Build xeus-xlang static library" ON)
OPTION(XEUS_XLANG_BUILD_SHARED "Split xlang_kernel build into executable and library" ON)
OPTION(XEUS_XLANG_BUILD_EXECUTABLE "Build the xlang_kernel executable" ON)

OPTION(XEUS_XLANG_USE_SHARED_XEUS "Link xlang_kernel  with the xeus shared library (instead of the static library)" ON)
OPTION(XEUS_XLANG_USE_SHARED_XEUS_XLANG "Link xlang_kernel  with the xeus shared library (instead of the static library)" ON)




# Dependencies
# ============


set(xeus_REQUIRED_VERSION 5.0.0)

find_package(xeus ${xeus_REQUIRED_VERSION} REQUIRED)

# Flags
# =====
include(CheckCXXCompilerFlag)

if (MSVC)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251 /wd4141")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4018 /wd4267 /wd4715 /wd4146 /wd4129")
endif ()

if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Intel")
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wunused-parameter -Wextra -Wreorder")
    

    CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP_14_FLAG)
    if (HAS_CPP_14_FLAG)
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
    else ()
        message(FATAL_ERROR "Unsupported compiler -- xeus requires C++14 support!")
    endif ()
endif ()



# Source files
# ============

set(XEUS_XLANG_HEADERS
    include/xeus-xlang/xeus_xlang_config.hpp
    include/xeus-xlang/xlang_interpreter.hpp
    include/xeus-xlang/httplib.h
)

set(XEUS_XLANG_SRC
    src/xlang_interpreter.cpp
)

set(XEUS_XLANG_MAIN_SRC
    src/main.cpp
)


# Targets and link - Macros
# =========================

include(CheckCXXCompilerFlag)

string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE)

set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib; ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}")

macro(xeus_xlang_set_common_options target_name)
    if (MSVC)
        target_compile_options(${target_name} PUBLIC /wd4251 /wd4141)
        target_compile_options(${target_name} PUBLIC /wd4018 /wd4267 /wd4715 /wd4146 /wd4129)
    endif ()

    if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR
        CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR
        CMAKE_CXX_COMPILER_ID MATCHES "Intel")

        target_compile_options(${target_name} PUBLIC -Wunused-parameter -Wextra -Wreorder)

        # C++14 flag
        CHECK_CXX_COMPILER_FLAG("-std=c++14" HAS_CPP_14_FLAG)
        if (HAS_CPP_14_FLAG)
            target_compile_features(${target_name} PRIVATE cxx_std_14)
        else ()
            message(FATAL_ERROR "Unsupported compiler -- xeus-xlang requires C++14 support!")
        endif ()
    endif ()

    if (APPLE)
        set_target_properties(${target_name} PROPERTIES
            MACOSX_RPATH ON
        )
    else ()
        set_target_properties(${target_name} PROPERTIES
            BUILD_WITH_INSTALL_RPATH 1
            SKIP_BUILD_RPATH FALSE
        )
    endif ()

    set_target_properties(${target_name} PROPERTIES
        INSTALL_RPATH_USE_LINK_PATH TRUE
    )
endmacro()

# Common macro kernels (xlang_kernel)
macro(xeus_xlang_set_kernel_options target_name)
    if (XEUS_XLANG_USE_SHARED_XEUS_XLANG)
        target_link_libraries(${target_name} PRIVATE xeus-xlang)
        if(CMAKE_DL_LIBS)
            target_link_libraries(${target_name} PRIVATE ${CMAKE_DL_LIBS} util)
        endif()
    else ()
        target_link_libraries(${target_name} PRIVATE xeus-xlang-static)
    endif()

    find_package(Threads)
        target_link_libraries(${target_name} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
    
endmacro()

# Common macro for shared and static library
macro(xeus_xlang_create_target target_name linkage output_name)
    string(TOUPPER "${linkage}" linkage_upper)

    if (NOT ${linkage_upper} MATCHES "^(SHARED|STATIC)$")
        message(FATAL_ERROR "Invalid library linkage: ${linkage}")
    endif ()

    add_library(${target_name} ${linkage_upper} ${XEUS_XLANG_SRC} ${XEUS_XLANG_HEADERS})
    xeus_xlang_set_common_options(${target_name})

    set_target_properties(${target_name} PROPERTIES
                          PUBLIC_HEADER "${XEUS_XLANG_HEADERS}"
                          PREFIX ""
                          VERSION ${${PROJECT_NAME}_VERSION}
                          SOVERSION ${XEUS_XLANG_VERSION_MAJOR}
                          OUTPUT_NAME "lib${output_name}")

    target_compile_definitions(${target_name} PUBLIC "XEUS_XLANG_EXPORTS")

    target_compile_features(${target_name} PRIVATE cxx_std_14)

    target_include_directories(${target_name}
                               PUBLIC
                               $<BUILD_INTERFACE:${XEUS_XLANG_INCLUDE_DIR}>
                               $<INSTALL_INTERFACE:include>)

    if (XEUS_XLANG_USE_SHARED_XEUS)
        set(XEUS_XLANG_XEUS_TARGET xeus)
    else ()
        set(XEUS_XLANG_XEUS_TARGET xeus-static)
    endif ()

    target_link_libraries(${target_name} PUBLIC ${XEUS_XLANG_XEUS_TARGET})
    if (WIN32 OR CYGWIN)
        #
    elseif (APPLE)
        target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup")
    endif ()
    find_package(Threads) # TODO: add Threads as a dependence of xeus-static?
        target_link_libraries(${target_name} PRIVATE ${CMAKE_THREAD_LIBS_INIT})
    
endmacro()

# xeus-xlang
# ===========

set(XEUS_XLANG_TARGETS "")

if (XEUS_XLANG_BUILD_SHARED)
    # Build libraries
    xeus_xlang_create_target(xeus-xlang SHARED xeus-xlang)
    list(APPEND XEUS_XLANG_TARGETS xeus-xlang)
endif ()

if (XEUS_XLANG_BUILD_STATIC)
    # On Windows, a static library should use a different output name
    # to avoid the conflict with the import library of a shared one.
    if (CMAKE_HOST_WIN32)
        xeus_xlang_create_target(xeus-xlang-static STATIC xeus-xlang-static)
    else ()
        xeus_xlang_create_target(xeus-xlang-static STATIC xeus-xlang)
    endif ()
    if(EMSCRIPTEN)
        target_compile_options(xeus-xlang-static PRIVATE -fPIC)
    endif()
    list(APPEND XEUS_XLANG_TARGETS xeus-xlang-static)
endif ()

# xlang_kernel
# =======
if (XEUS_XLANG_BUILD_EXECUTABLE)
    find_package(xeus-zmq 1.0.2 REQUIRED)
    add_executable(xlang_kernel ${XEUS_XLANG_MAIN_SRC})
    target_compile_features(xlang_kernel PRIVATE cxx_std_14)
    xeus_xlang_set_common_options(xlang_kernel)
    xeus_xlang_set_kernel_options(xlang_kernel)
    target_link_libraries(xlang_kernel PRIVATE xeus-zmq)
endif()



# Installation
# ============
include(CMakePackageConfigHelpers)

set(XEUS_XLANG_CMAKECONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" CACHE STRING "install path for xeus-xlangConfig.cmake")

# Install xeus-xlang and xeus-xlang-static
if (XEUS_XLANG_BUILD_SHARED)
    install(TARGETS ${XEUS_XLANG_TARGETS}
            EXPORT ${PROJECT_NAME}-targets
            ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
            PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xeus-xlang)

    # Makes the project importable from the build directory
    export(EXPORT ${PROJECT_NAME}-targets
           FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
endif ()

# Install xlang_kernel
if (XEUS_XLANG_BUILD_EXECUTABLE)
    install(TARGETS xlang_kernel
            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()

if (XEUS_XLANG_BUILD_EXECUTABLE OR EMSCRIPTEN)
    # Configuration and data directories for jupyter and xeus-xlang
    set(XJUPYTER_DATA_DIR "share/jupyter"    CACHE STRING "Jupyter data directory")

    # Install xlang_kernel Jupyter kernelspec
    set(KERNELSPEC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/share/jupyter/kernels)
    install(DIRECTORY ${KERNELSPEC_DIR}
            DESTINATION ${XJUPYTER_DATA_DIR}
            PATTERN "*.in" EXCLUDE)


    # Extra path for installing Jupyter kernelspec
    if (XEXTRA_JUPYTER_DATA_DIR)
        install(DIRECTORY ${KERNELSPEC_DIR}
                DESTINATION ${XEXTRA_JUPYTER_DATA_DIR}
                PATTERN "*.in" EXCLUDE)
    endif ()
endif ()

# Configure 'xeus-xlangConfig.cmake' for a build tree
set(XEUS_XLANG_CONFIG_CODE "####### Expanded from \@XEUS_XLANG_CONFIG_CODE\@ #######\n")
set(XEUS_XLANG_CONFIG_CODE "${XEUS_XLANG_CONFIG_CODE}set(CMAKE_MODULE_PATH \"${CMAKE_CURRENT_SOURCE_DIR}/cmake;\${CMAKE_MODULE_PATH}\")\n")
set(XEUS_XLANG_CONFIG_CODE "${XEUS_XLANG_CONFIG_CODE}##################################################")
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
                              "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
                              INSTALL_DESTINATION ${PROJECT_BINARY_DIR})

# Configure 'xeus-xlangConfig.cmake.in for an install tree
set(XEUS_XLANG_CONFIG_CODE "")
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
                              "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}Config.cmake"
                              INSTALL_DESTINATION ${XEUS_XLANG_CMAKECONFIG_INSTALL_DIR})

write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
                                 VERSION ${${PROJECT_NAME}_VERSION}
                                 COMPATIBILITY AnyNewerVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${PROJECT_NAME}Config.cmake
              ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
              DESTINATION ${XEUS_XLANG_CMAKECONFIG_INSTALL_DIR})

if (XEUS_XLANG_BUILD_SHARED)
    install(EXPORT ${PROJECT_NAME}-targets
            FILE ${PROJECT_NAME}Targets.cmake
            DESTINATION ${XEUS_XLANG_CMAKECONFIG_INSTALL_DIR})
endif ()


